package com.cellulam.framework.datasource.service;

import com.cellulam.framework.core.exceptions.BusinessException;
import com.cellulam.framework.core.exceptions.SysException;
import com.cellulam.framework.core.model.PagerCriteria;
import com.cellulam.framework.core.utils.ClassUtil;
import com.cellulam.framework.datasource.FetchDataBean;
import com.cellulam.framework.datasource.dao.CrudDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 基础Service类
 */
public abstract class BaseCrudService<PK, E extends Serializable> {

    @Autowired
    private PlatformTransactionManager transactionManager;

    protected Class<E> entityClass;

    public BaseCrudService() {
        entityClass = ClassUtil.getSuperClassGenericType(getClass(), 1);
    }

    /***
     * 通过<code>主键</code>获取数据Bean
     * @param id
     * @return
     */
    public E getBeanById(PK id) {
        return getCrudDao().getById(id);
    }

    /**
     * 保存数据Bean
     *
     * @param bean
     * @return 返回影响数据表的行数
     */
    public int saveBean(E bean) {
        return getCrudDao().save(bean);
    }

    /**
     * 更新数据Bean
     *
     * @param bean
     * @return 返回影响数据表的行数
     */
    public int updateBean(E bean) {
        return getCrudDao().update(bean);
    }


    /**
     * 根据<code>criteria</code>查询全部
     *
     * @param criteria
     * @return
     */
    public List<E> findBeans(E criteria) {
        return findBeans(criteria, 0, Integer.MAX_VALUE);
    }

    /**
     * 根据<code>criteria</code>和<code>offset, limit</code>来查询Beans
     *
     * @param criteria
     * @return
     */
    public List<E> findBeans(E criteria, int offset, int limit) {
        PagerCriteria<E> pagerCriteria = new PagerCriteria<>(limit, offset);
        pagerCriteria.setCriteria(criteria);
        return getCrudDao().query(pagerCriteria);
    }

    /**
     * 根据条件, 仅查询出一个Bean
     *
     * @param criteria
     * @return
     */
    public E findOneBean(E criteria) {
        List<E> beans = findBeans(criteria, 0, 1);
        return CollectionUtils.isEmpty(beans) ? null : beans.get(0);
    }

    /**
     * 获取数据
     *
     * @param start
     * @param limit
     * @param criteria
     * @return
     */
    public List<FetchDataBean<E>> fetch(long start, int limit, E criteria) {
        return this.getCrudDao().fetch(start, limit, criteria);
    }


    public long count(E criteria) {
        PagerCriteria<E> pagerCriteria = new PagerCriteria<>(criteria);
        return this.getCrudDao().count(pagerCriteria);
    }

    /**
     * 是否存在
     *
     * @param criteria
     * @return
     */
    public boolean exist(E criteria) {
        E bean = this.findOneBean(criteria);
        return bean != null;
    }

    /**
     * 锁行执行
     *
     * @param consumer
     */
    public void lockForExecute(PK id, Consumer<E> consumer) {
        this.executeInTran(() -> {
            E bean = this.getCrudDao().lockById(id);
            consumer.accept(bean);
        });
    }

    /**
     * 锁行执行，带返回值
     *
     * @param id
     * @param function
     * @param <T>
     * @return
     */
    public <T> T lockForSubmit(PK id, Function<E, T> function) {
        return this.commitInTran(() -> {
            E bean = this.getCrudDao().lockById(id);
            return function.apply(bean);
        });
    }

    /**
     * 事务执行
     */
    protected <T> T commitInTran(Callable<T> callable) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus transaction = transactionManager.getTransaction(def);

        try {
            T result = callable.call();
            transactionManager.commit(transaction);
            return result;
        } catch (BusinessException e) {
            transactionManager.rollback(transaction);
            throw e;
        } catch (Exception e) {
            transactionManager.rollback(transaction);
            throw new SysException(e);
        }
    }

    /**
     * 事务执行
     */
    protected void executeInTran(Runnable runnable) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus transaction = transactionManager.getTransaction(def);

        try {
            runnable.run();
            transactionManager.commit(transaction);
        } catch (BusinessException e) {
            transactionManager.rollback(transaction);
            throw e;
        } catch (Exception e) {
            transactionManager.rollback(transaction);
            throw new SysException(e);
        }
    }

    /**
     * 该service对应的dao
     *
     * @return
     */
    protected abstract CrudDao<PK, E> getCrudDao();
}
